/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pop3;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * @author epulapp
 */
public class ThreadPOP implements Runnable {

    /**
     * @param args the command line arguments
     */
    Socket socket;
    int etat; // 0 = init, 1 = autorisation, 2 = transaction
    BufferedReader br;
    BufferedWriter bw;
    File tempDir;
    ReentrantLock lock;
    boolean quit;
    
    HashMap<Integer, String> listMessages;
    
    public ThreadPOP(Socket socket) {
        this.socket = socket;
        try {
            this.bw = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream()));
            this.listMessages = new HashMap<Integer, String> ();
            this.br = null;//new BufferedReader(new InputStreamReader(socket.getInputStream()));
            this.tempDir = null;
            this.quit = false;
            this.etat = 0;
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
    
    public void run() {
        if (this.socket.isConnected()) {
            try {
                this.ecrireReponse("+OK Server ready\r\n");
                this.etat = 1;
                while (!this.quit) {
                    this.actionRequest();
                }
            } finally {
                try {
                    this.socket.close();
                } catch (Exception e) {}
            }
        } else {
            System.out.println("Le socket n'est pas connecté.");
        }
        
    }
    
    public void actionRequest() {
        if (this.br == null) {
            System.out.println("crée le flux de lecture du thread");
            try {
                this.br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            } catch (IOException ioe) {
                System.out.println("Impossible de lire après création du buffer de lecture");
                this.cleanQuit();
            }
        }
        try {
            
            String message = this.br.readLine();
            
            StringTokenizer st = new StringTokenizer(message);
            String action = st.nextToken();
            String info = null;
            if (st.hasMoreTokens()) {
                info = st.nextToken("\r\n");
            } else {
                info = "";
            }
            if (action.toUpperCase().equals("APOP")) {
                this.apop(info);
            } else if (action.toUpperCase().equals("RETR")) {
                this.retr(info);
            } else if (action.toUpperCase().equals("STAT")) {
                this.stat(info);
            } else if (action.toUpperCase().equals("QUIT")) {
                this.quit(info);
            }   
        } catch (IOException ioe) {
            this.cleanQuit();
        } catch (Exception e) {}
    }
    
    public void apop(String info) {
        if (this.etat == 1) {
            // ici, la connexion est autorisée dans tous les cas
            this.etat = 2;
            
            this.fetchMessages();
            
            this.ecrireReponse("+OK Bienvenue\r\n");
        } else {
            this.ecrireReponse("-ERR Connexion non autorise ou deja etablie\r\n");
        }
    }
    
    private void fetchMessages() {
        File courrier = new File("boite/courrier");
        
        try {
            if (courrier.isFile()) {
                MessageHandler mh = new MessageHandler();
                listMessages = mh.extractMessages(new FileReader(courrier));
            }
        } catch (FileNotFoundException fnfe) {
            this.ecrireReponse("-ERR Une erreur intèrne s'est produite.\r\n");
        } catch (IOException ioe) {
            this.ecrireReponse("-ERR Une erreur intèrne s'est produite.\r\n");
        }
    }
    
    public void retr(String info) {
        if (this.etat == 2) {
            BufferedReader bfr = null;
            try {
                int nMessage = Integer.parseInt(info.trim());
                System.out.println(nMessage);
                if (this.listMessages.containsKey(nMessage)) {
                    String message = this.listMessages.get(nMessage);
                    if (message != null) {
                        StringBuilder sb = new StringBuilder("+OK\r\n");
                        sb.append(message);
                        this.ecrireReponse(sb.toString());
                    } else {
                        this.ecrireReponse("-ERR Le message demandé n'existe pas.\r\n");
                    }
                } else {
                    this.ecrireReponse("-ERR Le message demandé n'existe pas.\r\n");
                }
            } catch (NumberFormatException nfe) {
                this.ecrireReponse("-ERR Paramètre invalide\r\n");
            } finally {
                try {
                    bfr.close();
                } catch (Exception e) {}
            }
        } else {
            this.ecrireReponse("-ERR Opération impossible\r\n");
        }
    }
    
    public void stat(String info) {
        if (this.etat == 2) {
            StringBuilder sb = new StringBuilder();
            sb.append("+OK");
            int count = 0;
            int tailleOctet = 0;
            try {
                for(Entry<Integer, String> entre : this.listMessages.entrySet()) {
                    if (entre.getValue() != null) {
                        count++;
                        tailleOctet += entre.getValue().getBytes().length;
                    }
                }
            } catch (SecurityException se) {}
            
            sb.append(" ").append(count).append(" ").append(tailleOctet).append("\r\n");

            this.ecrireReponse(sb.toString());
        } else {
            this.ecrireReponse("-ERR Opération impossible\r\n");
        }
    }
    
    public void quit(String info) throws IOException{
        try {
            this.ecrireReponse("+OK Aucun message supprimé\r\n");
        } finally {
            this.quit = true;
            this.cleanQuit();
        }
    }
    
    protected void ecrireReponse(String rep) {
        try {
            this.bw.write(rep);
            this.bw.flush();
        } catch (IOException ioe) {}
    }
    
    protected void cleanQuit() {
        try {
            if (this.bw != null) {
                this.bw.close();
            }
            if (this.br != null) {
                this.br.close();
            }
            this.quit = true;
            this.socket.close();
            
            System.out.println("On ferme les tunnels et supprime les fichiers temp proprement");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
